
void StartWebServer();

unsigned char RelayPin[LIGHT_NUMBER]; // = {RELAY1,RELAY2};
unsigned char TOUCHLED_PIN[LIGHT_NUMBER]; //触摸按键pin
unsigned char CONTROL_PIN[LIGHT_NUMBER];  //按键开关pin
unsigned char CONTROL_STAT[LIGHT_NUMBER]; //按键开关状态处理

const int threshold_top = 20;   //触摸顶部阈值
const int threshold_bottom = 1;   //触摸底部阈值，越接近数值越小
const int threshold_count = 4;   //触摸计数器有效值，通常会有意外的自动触发

int touchread[4] = {100, 100, 100, 100}; //初始化触摸读取值为100，无触摸
int touchDetected[4] = {}; //通过touchdetected持续计数判断是否按键，防止无触碰触发

bool touch_touched[4] = {};   //单击判断，作为灯开关的判断
int touch_touched_times[4] = {};  //单击次数，单击切换模式，双击
int touch_touching_time[4] = {}; //持续触摸秒数，用于判断长按事件，长按关闭，长按开启，开启状态长按调光，
bool touch_STATE[4] = {0, 0, 0, 0}; // 定义按键触发对象状态变量初始值，true开启

const double min_voltage  = 6;  //电池检测最低电压
double bat_voltage;
double pressure_val = 0; //气压数据 如果设置初始值为-1则不检测气压 传感器强制执行
int running_type = 0; //运行状态 0不运行 1篮球 2摩托车 3汽车 4自行车 5自定义
uint32_t running_time;
int pressure_type[6] = {600000, 1305485, 3762277, 3839579, 5691781, 1305485}; //气压类型 0不运行 1篮球 2摩托车 3汽车 4自行车
float running_pressure_type;

int ndrdy = SDA_PIN; //SDA
int clck = SCL_PIN;  //SCL

unsigned long TenthSecondsSinceStart = 0;
void TenthSecondsSinceStartTask();
void OnTenthSecond();
void OnSecond();

WebServer ESP32Server(80);

double return_voltage_value(int pin_no)
{
  double tmp = 0;
  double ADCVoltage = 0;
  double inputVoltage = 0;
  double avg = 0;
  for (int i = 0; i < 50; i++)
  {
    tmp = tmp + analogRead(pin_no);
  }
  avg = tmp / 50;
  ADCVoltage = ((avg * 3.3) / (4095)) + 0.138;
  inputVoltage = ADCVoltage / (R2_VOLTAGE / (R1_VOLTAGE + R2_VOLTAGE)); // formula for calculating voltage in i.e. GND
  return inputVoltage;
}

//触摸感应处理
void touchAttach(int touchID, uint8_t touchPin) {
  touchread[touchID] = touchRead(touchPin);
  if ( touchread[touchID] <= threshold_top && touchread[touchID] > threshold_bottom ) { //达到触发值的计数
    delay(38);  // 0.038秒
    touchDetected[touchID]++; //持续触摸计数
    if ( (touchDetected[touchID] >= threshold_count) && digitalRead(TOUCHLED_PIN[touchID]) == HIGH  ) {  //达到触发值的，灯不亮则亮灯
      //digitalWrite(TOUCHLED_PIN[touchID], LOW);
    }
  } else if (touchread[touchID] > threshold_top) { //无触摸处理
    if ( digitalRead(TOUCHLED_PIN[touchID]) == LOW ) { //灭触摸灯
      //digitalWrite(TOUCHLED_PIN[touchID], HIGH);
    }
    if ( touchDetected[touchID] >= threshold_count ) {  //检测无触摸之前的有效计数，触摸过则标记
      touch_touched[touchID] = true;
      touch_touched_times[touchID]++;  //触摸计数+1
    }
    touch_touching_time[touchID] = 0;  //持续触摸时间清零
    touchDetected[touchID]    = 0;  //持续触摸计数清零
  }
}

//按键开关的防抖处理判断
void controlAttach(int touchID, int touch_type = 0) { // 0 常开或常闭开关 1 轻触开关
  //按键开关，根据是否跟上一次状态相同作出判断，如果当前相同则不动作
  if ( CONTROL_STAT[touchID] == !digitalRead(CONTROL_PIN[touchID])) {

    int val1 = digitalRead(CONTROL_PIN[touchID]);
    delay(38);  // 0.038秒
    int val11 = digitalRead(CONTROL_PIN[touchID]);
    if (val11 != val1) {
      delay(15);
      val11 = digitalRead(CONTROL_PIN[touchID]);
    }
    delay(168);  // 故意不要连续读取
    if (val1 == val11) { // 确定不是抖动
      if ( CONTROL_STAT[touchID] == !digitalRead(CONTROL_PIN[touchID])) {
        touch_touched[touchID] = true;
        touch_touched_times[touchID]++;
        if (touch_type == 0) //常开或常闭开关更新状态，轻触开关则不更新
          CONTROL_STAT[touchID]  = digitalRead(CONTROL_PIN[touchID]);
      }
    }
  }
}

long readADC()
{
  if (digitalRead(ndrdy))
    return -1;

  long result = 0;

  for (int i = 0; i < 24; i++)
  {
    digitalWrite(clck, HIGH);
    delayMicroseconds(1);
    int new_bit = digitalRead(ndrdy);
    digitalWrite(clck, LOW);
    delayMicroseconds(1);
    result <<= 1;
    result |= new_bit;
  }

  for (int i = 0; i < 3; i++)
  {
    digitalWrite(clck, HIGH);
    delayMicroseconds(1);
    digitalWrite(clck, LOW);
    delayMicroseconds(1);
  }
  return result;
}

String TimeString(int TimeMillis) {

  String stringTime;

  stringTime +=  int(TimeMillis / 60 / 60);
  stringTime +=  ":";
  stringTime +=  int(TimeMillis / 60 % 60);
  stringTime +=  ":";
  stringTime +=  TimeMillis % 60;

  return stringTime;
}

String ProcessUpdate()    //页面更新
{
  //自动生成一串用“,”隔开的字符串。
  //HTML脚本会按照“, ”分割，形成一个字符串数组。
  //并把这个数组填个表格的相应部分。
  String ReturnString;
  if (touch_STATE[0]) {
    ReturnString += TimeString((millis() - running_time) / 1000);
  } else {
    ReturnString += "0";
  }
  ReturnString += ",";
  ReturnString += bat_voltage;  //电压值
  ReturnString += ",";
  ReturnString += pressure_val; //气压值
  ReturnString += ",";
  ReturnString += touch_STATE[0]; //运行状态
  ReturnString += ",";
  ReturnString += running_type; //运行方式
  ReturnString += ",";
  ReturnString += running_pressure_type; //运行气压自定义
  ReturnString += ",";
  ReturnString += TimeString(millis() / 1000); //系统运行时间

  Serial.println(ReturnString);
  return ReturnString;
}

void setup()
{
  delay(50);

  Serial.begin(115200);
  Serial.println("");

  while (!Serial) {}

  if (pressure_val != -1) {
    pinMode(ndrdy, INPUT);
    digitalWrite(ndrdy, LOW);

    pinMode(clck, OUTPUT);
    digitalWrite(clck, LOW);

    while (digitalRead(ndrdy)) {}
    while (!digitalRead(ndrdy)) {}
  }

  CONTROL_PIN[0] = CONTROL_1_PIN;  //按键
  CONTROL_PIN[1] = CONTROL_2_PIN;
  CONTROL_PIN[2] = CONTROL_3_PIN;
  TOUCHLED_PIN[0] = TOUCHLED_1_PIN; //指示灯
  TOUCHLED_PIN[1] = TOUCHLED_2_PIN;
  TOUCHLED_PIN[2] = TOUCHLED_3_PIN;
  for (byte i = 0; i < LIGHT_NUMBER; i++)
  {
    //机械开关 接地 低电平触发
    pinMode(CONTROL_PIN[i], INPUT_PULLUP); //设置按键管脚上拉输入模式
    pinMode(TOUCHLED_PIN[i], OUTPUT);
    digitalWrite(TOUCHLED_PIN[i], HIGH); //低电平有效
    CONTROL_STAT[i] = digitalRead(CONTROL_PIN[i]);  //开关所在状态读取，当前可能为高或者低
  }

  RelayPin[0] = LIGHT_1_PIN;
  RelayPin[1] = LIGHT_2_PIN;
  RelayPin[2] = LIGHT_3_PIN;

  for (byte i = 0; i < LIGHT_NUMBER; i++)
  {
    pinMode(RelayPin[i], OUTPUT);
    digitalWrite(RelayPin[i], HIGH); //低电平有效
  }

  uint32_t chipId = 0;
  for (int i = 0; i < 17; i = i + 8) {
    chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
  }
  Serial.printf("Chip ID: %d\r\n", chipId);

  Serial.printf("ESP32 Chip ID = %04X", (uint16_t)(ESP.getEfuseMac() >> 32)); //print High 2 bytes
  Serial.printf("%08X\r\n", (uint32_t)ESP.getEfuseMac()); //print Low 4bytes.

  Serial.printf("Chip model = %s Rev %d\r\n", ESP.getChipModel(), ESP.getChipRevision());
  Serial.printf("This chip has %d cores CpuFreqMHz = %u\r\n", ESP.getChipCores(), ESP.getCpuFreqMHz());
  Serial.printf("get Cycle Count = %u\r\n", ESP.getCycleCount());
  Serial.printf("SDK version:%s\r\n", ESP.getSdkVersion());  //获取IDF版本

  //获取片内内存  Internal RAM
  Serial.printf("Total heap size = %u\t", ESP.getHeapSize());
  Serial.printf("Available heap = %u\r\n", ESP.getFreeHeap());
  Serial.printf("Lowest level of free heap since boot = %u\r\n", ESP.getMinFreeHeap());
  Serial.printf("Largest block of heap that can be allocated at once = %u\r\n", ESP.getMaxAllocHeap());

  //SPI RAM
  Serial.printf("Total Psram size = %u\t", ESP.getPsramSize());
  Serial.printf("Available Psram = %u\r\n", ESP.getFreePsram());
  Serial.printf("Lowest level of free Psram since boot = %u\r\n", ESP.getMinFreePsram());
  Serial.printf("Largest block of Psram that can be allocated at once = %u\r\n", ESP.getMinFreePsram());


  sprintf(mac_tmp, "%02X\r\n", (uint32_t)(ESP.getEfuseMac() >> (24) ));
  sprintf(mac_tmp, "ESP32-%c%c%c%c%c%c", mac_tmp[4], mac_tmp[5], mac_tmp[2], mac_tmp[3], mac_tmp[0], mac_tmp[1] );
  //wifi初始化
  WiFi.mode(WIFI_AP);
  while (!WiFi.softAP(ssid, password)) {}; //启动AP
  Serial.println("AP启动成功");
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.softAPIP());
  byte mac[6];
  WiFi.macAddress(mac);
  WiFi.setHostname(ServerName);
  Serial.printf("macAddress 0x%02X:0x%02X:0x%02X:0x%02X:0x%02X:0x%02X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  //以下是启动OTA，可以通过WiFi刷新固件
  ArduinoOTA.setHostname(ServerName);
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else { // U_SPIFFS
      type = "filesystem";
    }

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) {
      Serial.println("Auth Failed");
    } else if (error == OTA_BEGIN_ERROR) {
      Serial.println("Begin Failed");
    } else if (error == OTA_CONNECT_ERROR) {
      Serial.println("Connect Failed");
    } else if (error == OTA_RECEIVE_ERROR) {
      Serial.println("Receive Failed");
    } else if (error == OTA_END_ERROR) {
      Serial.println("End Failed");
    }
  });
  ArduinoOTA.begin();
  Serial.println("ATO Ready");

  Serial.println("");

  // 启动闪存文件系统
  if (SPIFFS.begin())
  {
    Serial.println("SPIFFS Started.");
  }
  else
  {
    Serial.println("SPIFFS Failed to Start.");
  }

  StartWebServer();
  Serial.println("System ready");
  Serial.println("-----------------------------------------------");
}

void loop()
{
  ArduinoOTA.handle();

  //触摸感应处理
  touchAttach(0, T4);
  touchAttach(1, T3);
  touchAttach(2, T2);

  for (byte i = 0; i < LIGHT_NUMBER; i++)
  {
    controlAttach(i, 1); //按键开关处理
    if (touch_touched[i]) {
      Serial.print(i);
      Serial.print(" control touched\t");
      running_type  = touch_touched_times[1] % 6;
      Serial.println(running_type);

      switch (i) {
        case 0: //启停控制
          if ( running_type > 0 && !touch_STATE[i]) { //有类型选择且未运行
            touch_STATE[i] = 1; //启动状态修改
            digitalWrite(RelayPin[i], LOW);
            running_time  = millis();
            Serial.println(" Start Running ");
          } else {
            touch_STATE[i] = 0;
            digitalWrite(RelayPin[i], HIGH);
            Serial.println(" Sop Running ");
          }
          break;
        case 1: //灯控制
          switch (running_type) {
            case 0: //  全暗不启动
              digitalWrite(TOUCHLED_PIN[2], HIGH);
              digitalWrite(TOUCHLED_PIN[1], HIGH);
              digitalWrite(TOUCHLED_PIN[0], HIGH);
              break;
            case 1: //篮球 0.6bar
              digitalWrite(TOUCHLED_PIN[2], LOW);
              digitalWrite(TOUCHLED_PIN[1], HIGH);
              digitalWrite(TOUCHLED_PIN[0], HIGH);
              break;
            case 2: //汽车2.5
              digitalWrite(TOUCHLED_PIN[2], HIGH);
              digitalWrite(TOUCHLED_PIN[1], LOW);
              digitalWrite(TOUCHLED_PIN[0], HIGH);
              break;
            case 3: //摩托车
              digitalWrite(TOUCHLED_PIN[2], LOW);
              digitalWrite(TOUCHLED_PIN[1], LOW);
              digitalWrite(TOUCHLED_PIN[0], HIGH);
              break;
            case 4: //自行车
              digitalWrite(TOUCHLED_PIN[2], HIGH);
              digitalWrite(TOUCHLED_PIN[1], HIGH);
              digitalWrite(TOUCHLED_PIN[0], LOW);
              break;
            case 5: //自定义
              digitalWrite(TOUCHLED_PIN[2], LOW);
              digitalWrite(TOUCHLED_PIN[1], LOW);
              digitalWrite(TOUCHLED_PIN[0], LOW);
              break;
          }
          break;
        case 2:

          break;
      }

      touch_touched[i] = false;
    }
  }

  if ( pressure_val != -1) { //传感器正常
    long adcValue = readADC();
    if(adcValue!=-1){
      pressure_val = adcValue/100000 * 0.0689476 - 0.23;
    }
    pressure_type[5] = (running_pressure_type + 0.23) * 100000 / 0.0689476;

    if (adcValue > 520000 ) {
      Serial.print("ADC reading:");
      Serial.print(adcValue);
      Serial.print("\t");
      Serial.print(pressure_val);
      Serial.print(" bar\t");
      Serial.println(pressure_type[5]);
    }

    if (touch_STATE[0]) { //启动 充气执行超过3分钟自动停止
      if (adcValue >= pressure_type[running_type] || ((millis() - running_time) / 1000) > 180 )  //气压达到设定值则停机
      {
        touch_STATE[0]  = 0;
        delay(500);
        digitalWrite(RelayPin[0], HIGH);
        Serial.println(" End of Run ");
        delay(500);
      }
    }
  } else { //无传感器强制启动，设定时间1分钟停机
    if (touch_STATE[0]) {
      if ( ((millis() - running_time) / 1000) > 60 ) {
        touch_STATE[0]  = 0;
        delay(500);
        digitalWrite(RelayPin[0], HIGH);
        Serial.println(" End of Run ");
        delay(500);
      }
    }
  }

  TenthSecondsSinceStartTask();
}

unsigned long LastMillis = 0;
void TenthSecondsSinceStartTask() //100ms
{
  ESP32Server.handleClient();
  delay(2);
  unsigned long CurrentMillis = millis();
  if (abs(int(CurrentMillis - LastMillis)) > 100)
  {
    LastMillis = CurrentMillis;
    TenthSecondsSinceStart++;
    OnTenthSecond();
  }
  yield();
}

bool battery_low = 0;
void OnSecond()
{
#if defined(INPUT_VOLTAGE_SENSE_PIN)  //电池电压检测
  bat_voltage = return_voltage_value(INPUT_VOLTAGE_SENSE_PIN);
  if (bat_voltage < min_voltage && !battery_low) {
    battery_low = 1;
    digitalWrite(RelayPin[0], HIGH);  //关闭电机
    while (battery_low) { //电池电压低闪灯
      if (millis() % 500 < 250) {
        digitalWrite(TOUCHLED_PIN[2], HIGH);
        digitalWrite(TOUCHLED_PIN[1], HIGH);
        digitalWrite(TOUCHLED_PIN[0], HIGH);
      } else {
        digitalWrite(TOUCHLED_PIN[2], LOW);
        digitalWrite(TOUCHLED_PIN[1], LOW);
        digitalWrite(TOUCHLED_PIN[0], LOW);
      }
    }
  }
#endif
}

void OnTenthSecond()  // 100ms 十分之一秒
{

  if (TenthSecondsSinceStart % 10 == 0) //10次为1秒
  {
    OnSecond();
  }
}




void PocessControl(int DeviceType, int DeviceIndex, int Operation, float Operation2)
{
  String ReturnString;

  if (DeviceType == 0)
  {
    int SysIndex = Operation / 3;

    if (Operation % 3 == 0)
    { //重新设置传感器
      pressure_val  = 0;
      pinMode(ndrdy, INPUT);
      digitalWrite(ndrdy, LOW);

      pinMode(clck, OUTPUT);
      digitalWrite(clck, LOW);

      while (digitalRead(ndrdy)) {}
      while (!digitalRead(ndrdy)) {}

    }
    else if (Operation % 3 == 1)
    {

    }
    else if (Operation % 3 == 2)
    {
      ReturnString += "系统重启，请等待重新连接";
      printf("Reboot...");
      esp_restart();
    }
  }
  if (DeviceType == 1)  //启停
  {
    if (Operation == 0)
    {
      running_pressure_type = Operation2;
      touch_touched[0]  = 1;  //启停按过
      touch_touched_times[0]  = DeviceIndex;
      touch_touched[1]  = 1;
      touch_touched_times[1]  = DeviceIndex;
    }
  }
  if (DeviceType == 2)  //类型选择
  {
    running_pressure_type = Operation2;
    touch_touched[1]  = 1;
    touch_touched_times[1]  = DeviceIndex;

  }
}


bool handleFileRead(String path) {            //处理主页访问
  String contentType = "text/html";

  if (SPIFFS.exists(path)) {                       // 如果访问的文件可以在SPIFFS中找到
    File file = SPIFFS.open(path, "r");          // 则尝试打开该文件
    ESP32Server.streamFile(file, contentType);   // 并且将该文件返回给浏览器
    file.close();                                // 并且关闭文件
    return true;                                 // 返回true
  }
  return false;                                    // 如果文件未找到，则返回false
}

void handleNotFound()
{
  // 获取用户请求网址信息
  String webAddress = ESP32Server.uri();
  int AutheTimes  = 0;
//Serial.println(pressure_val);

  if (!ESP32Server.authenticate(username, userpassword)) //校验用户是否登录
  {
    if (AutheTimes == 3) {
      ESP32Server.send(404, "text/plain", "Bye");
    } else {
      AutheTimes++;
      return ESP32Server.requestAuthentication(); //请求进行用户登录认证
    }
  }

  //打印出请求
  if (webAddress != "/update")
  {
    printf("%s\n", webAddress.c_str());
  }

  //如果是主页请求，则发送FLASH中的index.html文件
  if (webAddress.endsWith("/")) {                   // 如果访问地址以"/"为结尾
    webAddress = "/index.html";                     // 则将访问地址修改为/index.html便于SPIFFS访问

    // 通过handleFileRead函数处处理用户访问
    handleFileRead(webAddress);
  }
  else if (webAddress.endsWith("update"))
  {
    ESP32Server.send(200, "text/plain", ProcessUpdate());
  }
  else if (webAddress.startsWith("/Control"))
  {
    if (ESP32Server.args() == 3)
    {
      int DeviceType = ESP32Server.arg(0).toInt();
      int DeviceIndex = ESP32Server.arg(1).toInt();
      int Operation = ESP32Server.arg(2).toInt();
      float Operation2 = ESP32Server.arg(2).toFloat();
      if (DeviceType == 1) {
        Operation = 0;
      }

      printf("DeviceType:%d DeviceIndex:%d Operation:%d Operation2:%.2f\n", DeviceType, DeviceIndex, Operation, Operation2 );

      PocessControl(DeviceType, DeviceIndex, Operation, Operation2);
    }
    else
    {
      ESP32Server.send(404, "text/plain", "404 Not Found");
    }
  }
  else
  {
    ESP32Server.send(404, "text/plain", "404 Not Found");
  }
}

void StartWebServer()
{
  ESP32Server.begin();
  ESP32Server.onNotFound(handleNotFound);//将所有请求导向自己处理的代码
}